package com.koron.standard.patrolLine.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.BiMap;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.koron.common.core.business.datadict.utils.DataDictTool;
import com.koron.standard.bean.query.PageQuery;
import com.koron.standard.deviceClasssify.bean.po.Type;
import com.koron.standard.deviceClasssify.service.TypeService;
import com.koron.standard.patrolLine.bean.dto.LinePatrolDTO;
import com.koron.standard.patrolLine.bean.excel.LinePatrolExcel;
import com.koron.standard.patrolLine.bean.po.LinePatrol;
import com.koron.standard.patrolLine.bean.po.LinePatrolCheck;
import com.koron.standard.patrolLine.bean.po.LinePatrolRecord;
import com.koron.standard.patrolLine.bean.query.LinePatrolQuery;
import com.koron.standard.patrolLine.bean.vo.LinePatrolVO;
import com.koron.standard.patrolLine.mapper.LinePatrolMapper;
import com.koron.standard.utils.CommonUtils;
import com.koron.standard.utils.StandardCodeTools;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 巡视标准(Patrol)表服务实现类
 *
 * @author jzp
 * @since 2021-10-19
 */
@Service
public class LinePatrolService extends ServiceImpl<LinePatrolMapper, LinePatrol> {
    @Autowired
    LinePatrolCheckService linePatrolCheckService;

    @Autowired
    LinePatrolRecordService linePatrolRecordService;

    @Autowired
    TypeService typeService;

    public QueryWrapper<LinePatrolVO> queryWrapper(LinePatrolQuery query) {
        // 设置默认只查有权限的水司数据
        query.setAppids(CommonUtils.getDefaultAppids(query.getAppids()));
        query.setPublicStatus(CommonUtils.getDefaultPublicStatus(query.getPublicStatus()));

        QueryWrapper<LinePatrolVO> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotEmpty(query.getCode()), "c_line_patrol.code", query.getCode());
        wrapper.like(StrUtil.isNotEmpty(query.getName()), "c_line_patrol.name", query.getName());
        wrapper.like(StrUtil.isNotEmpty(query.getTypeName()), "c_type.name", query.getTypeName());
        // wrapper.eq(StrUtil.isNotEmpty(query.getTypeId()), "c_line_patrol.type_id", query.getTypeId());
        if (StrUtil.isNotEmpty(query.getTypeId()) || StrUtil.isNotEmpty(query.getTypeWrapId())) {
            List<Type> list;
            // 查询该设备分类下的所有子节点
            if (StrUtil.isNotEmpty(query.getTypeId())) {
                Type type = typeService.getById(query.getTypeId());
                Assert.notNull(type, "该设备分类不存在");
                // 查询该设备分类下的所有设备
                LambdaQueryWrapper<Type> typeWrapper = new LambdaQueryWrapper<>();
                typeWrapper.likeRight(Type::getCode, type.getCode());
                list = typeService.list(typeWrapper);
            } else {
                Type type = typeService.getById(query.getTypeWrapId());
                Assert.notNull(type, "该设备分类不存在");
                // 往上追溯所有节点，获取所有节点的code
                List<String> codeList = IntStream.rangeClosed(1, type.getCode().length() / 3)
                        .mapToObj(e -> type.getCode().substring(0, e * 3))
                        .sorted(Comparator.reverseOrder())
                        .collect(Collectors.toList());

                LambdaQueryWrapper<Type> typeWrapper = new LambdaQueryWrapper<>();
                typeWrapper.in(Type::getCode, codeList);
                list = typeService.list(typeWrapper);
            }

            if (CollUtil.isNotEmpty(list)) {
                wrapper.in("c_line_patrol.type_id", list.stream().map(Type::getId).collect(Collectors.toList()));
            }
        }

        if (query.getPublicStatus() != null && query.getPublicStatus() > 0) {
            // 如果是共有的
            wrapper.and(w -> w.eq("c_line_patrol.public_status", query.getPublicStatus())
                    .or(CollUtil.isNotEmpty(query.getAppids()))
                    .in(CollUtil.isNotEmpty(query.getAppids()), "c_line_patrol.appid", query.getAppids()));
        } else {
            wrapper.in(CollUtil.isNotEmpty(query.getAppids()), "c_line_patrol.appid", query.getAppids());
        }

        wrapper.eq(query.getStatus()!=null, "c_line_patrol.status", query.getStatus());
        wrapper.eq(StrUtil.isNotEmpty(query.getDeptId()), "c_line_patrol.dept_id", query.getDeptId());
        wrapper.like(StrUtil.isNotEmpty(query.getDeptName()), "c_line_patrol.dept_name", query.getDeptName());
        wrapper.eq(StrUtil.isNotEmpty(query.getCreateBy()), "c_line_patrol.create_by", query.getCreateBy());
        wrapper.like(StrUtil.isNotEmpty(query.getCreateByName()), "c_line_patrol.create_by_name", query.getCreateByName());
        if (CollUtil.isNotEmpty(query.getDates())) {
            wrapper.between("c_line_patrol.create_time", query.getDates().get(0), query.getDates().get(1));
        }
        wrapper.eq(StrUtil.isNotEmpty(query.getStandardType()), "c_line_patrol.standard_type", query.getStandardType());
        wrapper.eq(StrUtil.isNotEmpty(query.getParentRiskName()), "c_line_patrol.parent_risk_name", query.getParentRiskName());
        wrapper.eq(StrUtil.isNotEmpty(query.getRiskName()), "c_line_patrol.risk_name", query.getRiskName());
        wrapper.eq("c_line_patrol.delete_flag", 0);
        return wrapper;
    }


    public List<LinePatrolVO> list(LinePatrolQuery query) {
        QueryWrapper<LinePatrolVO> wrapper = queryWrapper(query);
        return getBaseMapper().selectPageVo(wrapper);
    }

    public Page<LinePatrolVO> page(PageQuery<LinePatrolVO> pageQuery, LinePatrolQuery query) {
        QueryWrapper<LinePatrolVO> wrapper = queryWrapper(query);
        Page<LinePatrolVO> page = pageQuery.convertPage();
        List<OrderItem> orders = pageQuery.getOrders();
        for (OrderItem order : orders) {
            if (order.getColumn().equals("type_name")) {
                order.setColumn("c_type.name");
            } else if (order.getColumn().equals("app_name")) {
                order.setColumn("s_app.name");
            } else {
                order.setColumn("c_line_patrol." + order.getColumn());
            }
        }
        getBaseMapper().selectPageVo(page, wrapper);
        return page;
    }

    public LinePatrolDTO getDtoById(String id) {
        LinePatrolVO patrolVO = getBaseMapper().getVoById(id);
        LinePatrolDTO linePatrolDTO = new LinePatrolDTO();
        BeanUtils.copyProperties(patrolVO, linePatrolDTO);
        List<LinePatrolCheck> checks = linePatrolCheckService.listByPatrolId(id);
        List<LinePatrolRecord> records = linePatrolRecordService.listByPatrolId(id);
        linePatrolDTO.setChecks(checks);
        linePatrolDTO.setRecords(records);
        return linePatrolDTO;
    }

    /**
     * 筛选除有标准的分类id
     */
    public List<String> filterTypeIds(List<String> typeIds) {
        LambdaQueryWrapper<LinePatrol> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CollUtil.isNotEmpty(typeIds), LinePatrol::getTypeId, typeIds);
        List<LinePatrol> list = list(wrapper);
        return list.stream().map(LinePatrol::getTypeId).distinct().collect(Collectors.toList());
    }

    public void saveDto(LinePatrolDTO linePatrolDTO) {
        // 设置编码
        linePatrolDTO.setCode(StandardCodeTools.getPatrolCode());

        // 默认草稿
        linePatrolDTO.setStatus(0);

        // 保存
        updateDtoById(linePatrolDTO);

    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void updateDtoById(LinePatrolDTO linePatrolDTO) {
        // 1. 删除所有子项
        linePatrolCheckService.removeByPatrolId(linePatrolDTO.getId());
        linePatrolRecordService.removeByPatrolId(linePatrolDTO.getId());

        // 2. 保存或更新基本信息
        saveOrUpdate(linePatrolDTO);

        // 3. 保存子项信息
        List<LinePatrolCheck> checks = Optional.ofNullable(linePatrolDTO.getChecks()).orElse(new ArrayList<>());
        int i = 0;
        for (LinePatrolCheck check : checks) {
            check.setPatrolId(linePatrolDTO.getId());
            check.setSort(i++);
        }
        linePatrolCheckService.saveOrUpdateBatch(checks);

        // 4. 保存子项信息
        List<LinePatrolRecord> records = Optional.ofNullable(linePatrolDTO.getRecords()).orElse(new ArrayList<>());
        int j = 0;
        for (LinePatrolRecord record : records) {
            record.setPatrolId(linePatrolDTO.getId());
            record.setSort(j++);
        }
        linePatrolRecordService.saveOrUpdateBatch(records);

    }

    public void updateToPublic(List<String> ids) {
        List<LinePatrol> linePatrols = new ArrayList<>();
        for (String id : ids) {
            LinePatrol linePatrol = new LinePatrol();
            linePatrol.setId(id);
            linePatrol.setPublicStatus(1);
            linePatrols.add(linePatrol);
        }
        updateBatchById(linePatrols);
    }

    public void updateStatus(String id, Integer status) {
        LambdaUpdateWrapper<LinePatrol> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LinePatrol::getId, id);
        wrapper.set(LinePatrol::getStatus, status);
        update(wrapper);
    }

    public List<LinePatrolExcel> listPatrolExcel(LinePatrolQuery linePatrolQuery) {
        List<LinePatrolExcel> result = new ArrayList<>();
        List<LinePatrolVO> patrols = list(linePatrolQuery);
        // 数据字典
        BiMap<String, String> standardTypeDict = new BiMap<>(DataDictTool.getDataDictByCode("standardManagement", "standard_patrol_type"));
        // 每50个id查询一次从表数据
        for (int i = 0; i < patrols.size(); i = i + 50) {
            List<LinePatrolVO> subPatrols = patrols.subList(i, Math.min((i + 50), patrols.size()));
            Map<String, LinePatrolVO> patrolMap = subPatrols.stream().collect(Collectors.toMap(LinePatrolVO::getId, e -> e, (o1, o2) -> o1, LinkedHashMap::new));
            Map<String, List<LinePatrolCheck>> patrolCheckMap = linePatrolCheckService.listByPatrolIds(new ArrayList<>(patrolMap.keySet()))
                    .stream()
                    .collect(Collectors.groupingBy(LinePatrolCheck::getPatrolId, LinkedHashMap::new, Collectors.toList()));
            Map<String, List<LinePatrolRecord>> patrolRecordMap = linePatrolRecordService.listByPatrolIds(new ArrayList<>(patrolMap.keySet()))
                    .stream()
                    .collect(Collectors.groupingBy(LinePatrolRecord::getPatrolId, LinkedHashMap::new, Collectors.toList()));
            patrolMap.forEach((patrolId, patrol) -> {
                List<LinePatrolCheck> checks = Optional.ofNullable(patrolCheckMap.get(patrolId)).orElse(new ArrayList<>());
                List<LinePatrolRecord> records = Optional.ofNullable(patrolRecordMap.get(patrolId)).orElse(new ArrayList<>());
                int maxSize = Math.max(checks.size(), records.size());
                for (int j = 0; j < maxSize; j++) {
                    LinePatrolExcel linePatrolExcel = new LinePatrolExcel();
                    // 巡视标准基本数据
                    linePatrolExcel.setTypeId(patrol.getTypeId());
                    linePatrolExcel.setTypeName(patrol.getTypeName());
                    linePatrolExcel.setName(patrol.getName());
                    linePatrolExcel.setStandardType(standardTypeDict.get(patrol.getStandardType()));

                    // 检查项数据
                    LinePatrolCheck linePatrolCheck = j < checks.size() ? checks.get(j) :new LinePatrolCheck();
                    linePatrolExcel.setCheckName(linePatrolCheck.getName());
                    linePatrolExcel.setCheckContent(linePatrolCheck.getContent());

                    // 数据记录项数据
                    LinePatrolRecord linePatrolRecord = j < records.size() ? records.get(j) :new LinePatrolRecord();
                    linePatrolExcel.setRecordName(linePatrolRecord.getName());
                    linePatrolExcel.setRecordUnit(linePatrolRecord.getUnit());

                    result.add(linePatrolExcel);
                }

            });
        }
        return result;
    }
}
